package dao

import (
	"context"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"jk-time/webook/pkg/migrator"
	"time"
)

var (
	ErrRecordnotFound = gorm.ErrRecordNotFound
)

type InteractiveDAO interface {
	IncrReadCnt(ctx context.Context, biz string, bizId int64) error
	InsertLikeInfo(ctx context.Context, cb UserLikeBiz) error
	GetLikeInfo(ctx context.Context, biz string, bizId, uid int64) (UserLikeBiz, error)
	DeleteLikeInfo(ctx context.Context, biz string, bizId, uid int64) error
	Get(ctx context.Context, biz string, bizId int64) (Interactive, error)
	InsertCollectionBiz(ctx context.Context, cb UserCollectionBiz) error
	GetCollectionInfo(ctx context.Context, biz string, bizId, uid int64) (UserCollectionBiz, error)
	BatchIncrReadCnt(ctx context.Context, bizs []string, ids []int64) error
	GetByIds(ctx context.Context, biz string, ids []int64) ([]Interactive, error)
}
type GormInteractiveDAO struct {
	db *gorm.DB
}

func NewGormInteractiveDAO(db *gorm.DB) InteractiveDAO {
	return &GormInteractiveDAO{db: db}
}
func (g *GormInteractiveDAO) IncrReadCnt(ctx context.Context, biz string, bizId int64) error {
	return g.incrReadCnt(g.db.WithContext(ctx), biz, bizId)
}
func (g *GormInteractiveDAO) incrReadCnt(tx *gorm.DB, biz string, bizId int64) error {
	var intr Interactive
	err := tx.Where("biz_id=? and biz=?", bizId, biz).
		First(&intr).Error
	if err != nil {
		return err
	}

	now := time.Now().UnixMilli()
	return g.db.Clauses(clause.OnConflict{
		DoUpdates: clause.Assignments(map[string]any{
			"read_cnt": gorm.Expr("read_cnt + 1"),
			"utime":    time.Now().UnixMilli(),
		}),
	}).Create(&Interactive{
		Biz:     biz,
		BizId:   bizId,
		ReadCnt: 1,
		Ctime:   now,
		Utime:   now,
	}).Error
}
func (g *GormInteractiveDAO) BatchIncrReadCnt(ctx context.Context, bizs []string,
	ids []int64) error {
	return g.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		for i := 0; i < len(bizs); i++ {
			err := g.incrReadCnt(tx, bizs[i], ids[i])
			if err != nil {
				return err
			}
		}
		return nil
	})
}
func (g *GormInteractiveDAO) InsertLikeInfo(ctx context.Context, cb UserLikeBiz) error {
	now := time.Now().UnixMilli()
	cb.Utime = now
	cb.Ctime = now
	cb.Status = 1
	err := g.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		return tx.Clauses(clause.OnConflict{
			DoUpdates: clause.Assignments(map[string]any{
				"utime":  now,
				"status": 1,
			}),
		}).Create(&cb).Error
	})
	if err != nil {
		return err
	}
	return g.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		return tx.Clauses(clause.OnConflict{
			DoUpdates: clause.Assignments(map[string]any{
				"like_cnt": gorm.Expr("like_cnt + 1"),
				"utime":    time.Now().UnixMilli(),
			}),
		}).Create(&Interactive{
			Biz:     cb.Biz,
			BizId:   cb.BizId,
			LikeCnt: 1,
			Ctime:   now,
			Utime:   now,
		}).Error
	})
}

func (g *GormInteractiveDAO) GetLikeInfo(ctx context.Context, biz string, bizId, uid int64) (UserLikeBiz, error) {
	var res UserLikeBiz
	err := g.db.WithContext(ctx).Where("biz=? and biz_id=? and uid=?", biz, bizId, uid).First(&res).Error
	if err == gorm.ErrRecordNotFound {
		return res, nil
	}
	return res, err
}

func (g *GormInteractiveDAO) DeleteLikeInfo(ctx context.Context, biz string, bizId, uid int64) error {
	now := time.Now().UnixMilli()
	return g.db.WithContext(ctx).Model(&UserLikeBiz{}).Transaction(func(tx *gorm.DB) error {
		err := tx.Where("uid=? and biz_id=? and biz=?", uid, bizId, biz).Updates(map[string]any{
			"utime":  now,
			"status": 0,
		}).Error
		if err != nil {
			return err
		}

		return tx.Model(&Interactive{}).
			Where("biz_id=? and biz=?", bizId, biz).Updates(map[string]any{
			"like_cnt": gorm.Expr("like_cnt - 1"),
			"utime":    now,
		}).Error
	})
}

func (g *GormInteractiveDAO) Get(ctx context.Context, biz string, bizId int64) (Interactive, error) {
	var intr Interactive
	err := g.db.WithContext(ctx).Where("biz=? and biz_id=?", biz, bizId).First(&intr).Error
	if err == gorm.ErrRecordNotFound {
		return intr, nil
	}
	return intr, err
}

func (g *GormInteractiveDAO) InsertCollectionBiz(ctx context.Context, cb UserCollectionBiz) error {
	now := time.Now().UnixMilli()
	cb.Ctime = now
	cb.Utime = now
	err := g.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		return tx.Clauses(clause.OnConflict{
			DoUpdates: clause.Assignments(map[string]any{
				"utime": now,
			}),
		}).Create(&cb).Error
	})
	if err != nil {
		return err
	}
	return g.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		return tx.Clauses(clause.OnConflict{
			DoUpdates: clause.Assignments(map[string]any{
				"coll_cnt": gorm.Expr("coll_cnt + 1"),
				"utime":    time.Now().UnixMilli(),
			}),
		}).Create(&Interactive{
			Biz:     cb.Biz,
			BizId:   cb.BizId,
			CollCnt: 1,
			Ctime:   now,
			Utime:   now,
		}).Error
	})
}

func (g *GormInteractiveDAO) GetCollectionInfo(ctx context.Context, biz string, bizId, uid int64) (UserCollectionBiz, error) {
	var res UserCollectionBiz
	err := g.db.WithContext(ctx).
		Where("biz=? AND biz_id = ? AND uid = ?", biz, bizId, uid).First(&res).Error
	if err == gorm.ErrRecordNotFound {
		return res, nil
	}
	return res, err
}
func (g *GormInteractiveDAO) GetByIds(ctx context.Context, biz string, ids []int64) ([]Interactive, error) {
	var res []Interactive
	err := g.db.WithContext(ctx).
		Where("biz=? AND biz_id in ?", biz, ids).Find(&res).Error
	if err == gorm.ErrRecordNotFound {
		return res, nil
	}
	return res, err
}

type Interactive struct {
	Id int64 `gorm:"primaryKey,autoIncrement"`
	// 在BizId和Biz 创建联合唯一索引
	BizId   int64  `gorm:"uniqueindex:bizId_biz,priority:1"`
	Biz     string `gorm:"uniqueindex:bizId_biz,priority:12;type:varchar(128)"`
	ReadCnt int64
	LikeCnt int64
	CollCnt int64

	Ctime int64
	Utime int64
}

func (i Interactive) CompareTo(dst migrator.Entity) bool {
	dstVal, _ := dst.(Interactive)
	return i == dstVal
}

func (i Interactive) ID() int64 {
	return i.Id
}

type UserLikeBiz struct {
	Id    int64  `gorm:"primaryKey,autoIncrement"`
	Biz   string `gorm:"uniqueindex:bizId_biz_uid,priority:3;type:varchar(128)"`
	BizId int64  `gorm:"uniqueindex:bizId_biz_uid,priority:2"`
	//谁 where uid and biz_id and biz
	Uid   int64 `gorm:"uniqueindex:bizId_biz_uid,priority:1"`
	Ctime int64
	Utime int64

	// 1- 有效，0-无效。软删除的用法
	Status uint8 `gorm:comment '1有效 0无效'`
}
type UserCollectionBiz struct {
	Id int64 `gorm:"primaryKey,autoIncrement"`
	//收藏夹id
	Cid int64 `gorm:"index"`
	//谁 where uid and biz_id and biz
	Biz   string `gorm:"uniqueindex:bizId_biz_uid,priority:3;type:varchar(128)"`
	BizId int64  `gorm:"uniqueindex:bizId_biz_uid,priority:2"`
	Uid   int64  `gorm:"uniqueindex:bizId_biz_uid,priority:1"`
	Ctime int64
	Utime int64
}

func (Interactive) TableName() string {
	return "interactive"
}
func (UserLikeBiz) TableName() string {
	return "user_like_biz"
}
func (UserCollectionBiz) TableName() string {
	return "user_collection_biz"
}
